using System.ComponentModel;
using System.Runtime.Serialization;

using SharpGL.Enumerations;

namespace SharpGL.OpenGLAttributes
{
    /// <summary>
    /// The polygon attributes settings.
    /// </summary>
    [TypeConverter(typeof(System.ComponentModel.ExpandableObjectConverter))]
    [DataContract(/*IsReference = true, */Name = "PolygonAttributes", Namespace = "SharpGL.SceneGraph")]
    public class PolygonAttributes : OpenGLAttributes
    {
        #region Private Consts

        private static readonly AttributeMask attributeMask = AttributeMask.Polygon;

        #endregion Private Consts

        #region Private Fields

        [DataMember()]
        private bool? enableCullFace;
        [DataMember()]
        private FaceMode? cullFaceMode;
        [DataMember()]
        private bool? enableSmooth;
        [DataMember()]
        private FrontFaceMode? frontFaceMode;
        [DataMember()]
        private PolygonMode? polygonMode;
        [DataMember()]
        private float? offsetFactor;
        [DataMember()]
        private float? offsetBias;
        [DataMember()]
        private bool? enableOffsetPoint;
        [DataMember()]
        private bool? enableOffsetLine;
        [DataMember()]
        private bool? enableOffsetFill;

        #endregion Private Fields

        #region Protected Methods

        protected override void OnDeserializing()
        {
            AttributeFlags = attributeMask;
        }

        #endregion Protected Methods

        #region Public Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="PolygonAttributes"/> class.
        /// </summary>
        public PolygonAttributes()
        {
            AttributeFlags = attributeMask;
        }

        #endregion Public Constructors

        #region Public Methods

        /// <summary>
        /// Returns true if any attributes are set.
        /// </summary>
        /// <returns>
        /// True if any attributes are set
        /// </returns>
        public override bool AreAnyAttributesSet()
        {
            return enableCullFace.HasValue ||
                cullFaceMode.HasValue ||
                enableSmooth.HasValue ||
                frontFaceMode.HasValue ||
                polygonMode.HasValue ||
                offsetFactor.HasValue ||
                offsetBias.HasValue ||
                enableOffsetPoint.HasValue ||
                enableOffsetLine.HasValue ||
                enableOffsetFill.HasValue;
        }

        /// <summary>
        /// Reset all attributes to unset state.
        /// </summary>
        public override void ResetAllAttributes()
        {
            enableCullFace = null;
            cullFaceMode = null;
            enableSmooth = null;
            frontFaceMode = null;
            polygonMode = null;
            offsetFactor = null;
            offsetBias = null;
            enableOffsetPoint = null;
            enableOffsetLine = null;
            enableOffsetFill = null;
        }

        /// <summary>
        /// Sets the attributes.
        /// </summary>
        /// <param name="gl">The OpenGL instance.</param>
        public override void SetAttributes(OpenGL gl)
        {
            if (enableCullFace.HasValue) gl.EnableIf(OpenGL.GL_CULL_FACE, enableCullFace.Value);
            if (cullFaceMode.HasValue) gl.CullFace((uint)cullFaceMode.Value);
            if (enableSmooth.HasValue) gl.EnableIf(OpenGL.GL_POLYGON_SMOOTH, enableSmooth.Value);
            if (frontFaceMode.HasValue) gl.FrontFace((uint)frontFaceMode.Value);
            if (polygonMode.HasValue) gl.PolygonMode(OpenGL.GL_FRONT_AND_BACK, (uint)polygonMode.Value);
            if (offsetFactor.HasValue && offsetBias.HasValue) gl.PolygonOffset(offsetFactor.Value, offsetBias.Value);
            if (enableOffsetPoint.HasValue) gl.EnableIf(OpenGL.GL_POLYGON_OFFSET_POINT, enableOffsetPoint.Value);
            if (enableOffsetLine.HasValue) gl.EnableIf(OpenGL.GL_POLYGON_OFFSET_LINE, enableOffsetLine.Value);
            if (enableOffsetFill.HasValue) gl.EnableIf(OpenGL.GL_POLYGON_OFFSET_FILL, enableOffsetFill.Value);
        }

        #endregion Public Methods

        #region Public Properties

        /// <summary>
        /// Gets or sets the enable cull face.
        /// </summary>
        /// <value>
        /// The enable cull face.
        /// </value>
        [Description("EnableCullFace"), Category("Polygon attributes")]
        public bool? EnableCullFace
        {
            get { return enableCullFace; }
            set
            {
                if (enableCullFace != value)
                {
                    enableCullFace = value;

                    NotifyPropertyChanged("EnableCullFace");
                }
            }
        }

        /// <summary>
        /// Gets or sets the enable smooth.
        /// </summary>
        /// <value>
        /// The enable smooth.
        /// </value>
        [Description("EnableSmooth"), Category("Polygon attributes")]
        public bool? EnableSmooth
        {
            get { return enableSmooth; }
            set
            {
                if (enableSmooth != value)
                {
                    enableSmooth = value;

                    NotifyPropertyChanged("EnableSmooth");
                }
            }
        }

        /// <summary>
        /// Gets or sets the cull faces.
        /// </summary>
        /// <value>
        /// The cull faces.
        /// </value>
        [Description("CullFaces"), Category("Polygon attributes")]
        public FaceMode? CullFaces
        {
            get { return cullFaceMode; }
            set
            {
                if (cullFaceMode != value)
                {
                    cullFaceMode = value;

                    NotifyPropertyChanged("CullFaces");
                }
            }
        }

        /// <summary>
        /// Gets or sets the front faces.
        /// </summary>
        /// <value>
        /// The front faces.
        /// </value>
        [Description("FrontFaces"), Category("Polygon attributes")]
        public FrontFaceMode? FrontFaces
        {
            get { return frontFaceMode; }
            set
            {
                if (frontFaceMode != value)
                {
                    frontFaceMode = value;

                    NotifyPropertyChanged("FrontFaces");
                }
            }
        }

        /// <summary>
        /// Gets or sets the polygon draw mode.
        /// </summary>
        /// <value>
        /// The polygon draw mode.
        /// </value>
        [Description("PolygonMode"), Category("Polygon attributes")]
        public PolygonMode? PolygonMode
        {
            get { return polygonMode; }
            set
            {
                if (polygonMode != value)
                {
                    polygonMode = value;

                    NotifyPropertyChanged("PolygonMode");
                }
            }
        }

        /// <summary>
        /// Gets or sets the offset factor.
        /// </summary>
        /// <value>
        /// The offset factor.
        /// </value>
        [Description("OffsetFactor"), Category("Polygon attributes")]
        public float? OffsetFactor
        {
            get { return offsetFactor; }
            set
            {
                if (offsetFactor != value)
                {
                    offsetFactor = value;

                    NotifyPropertyChanged("OffsetFactor");
                }
            }
        }

        /// <summary>
        /// Gets or sets the offset bias.
        /// </summary>
        /// <value>
        /// The offset bias.
        /// </value>
        [Description("OffsetBias"), Category("Polygon attributes")]
        public float? OffsetBias
        {
            get { return offsetBias; }
            set
            {
                if (offsetBias != value)
                {
                    offsetBias = value;

                    NotifyPropertyChanged("OffsetBias");
                }
            }
        }

        /// <summary>
        /// Gets or sets the enable offset point.
        /// </summary>
        /// <value>
        /// The enable offset point.
        /// </value>
        [Description("EnableOffsetPoint"), Category("Polygon attributes")]
        public bool? EnableOffsetPoint
        {
            get { return enableOffsetPoint; }
            set
            {
                if (enableOffsetPoint != value)
                {
                    enableOffsetPoint = value;

                    NotifyPropertyChanged("EnableOffsetPoint");
                }
            }
        }

        /// <summary>
        /// Gets or sets the enable offset line.
        /// </summary>
        /// <value>
        /// The enable offset line.
        /// </value>
        [Description("EnableOffsetLine"), Category("Polygon attributes")]
        public bool? EnableOffsetLine
        {
            get { return enableOffsetLine; }
            set
            {
                if (enableOffsetLine != value)
                {
                    enableOffsetLine = value;

                    NotifyPropertyChanged("EnableOffsetLine");
                }
            }
        }

        /// <summary>
        /// Gets or sets the enable offset fill.
        /// </summary>
        /// <value>
        /// The enable offset fill.
        /// </value>
        [Description("EnableOffsetFill"), Category("Polygon attributes")]
        public bool? EnableOffsetFill
        {
            get { return enableOffsetFill; }
            set
            {
                if (enableOffsetFill != value)
                {
                    enableOffsetFill = value;

                    NotifyPropertyChanged("EnableOffsetFill");
                }
            }
        }

        #endregion Public Properties
    }
}
